package heap

import (
	"fmt"
	"jvm/classfile"
	"jvm/classpath"
	. "jvm/typedef"
)

// ***********************************************************
type ClassLoader struct {
	classPath *classpath.Classpath
	classMap  map[string]*Class
	logClass  bool
}

func NewClassLoader(classPath *classpath.Classpath, logClass bool) *ClassLoader {
	loader := &ClassLoader{
		classPath: classPath,
		classMap:  make(map[string]*Class),
		logClass:  logClass,
	}
	loader.loadBasicClasses()
	loader.loadPrimitiveClasses()
	return loader
}

// ***********************************************************
func (self *ClassLoader) loadBasicClasses() {
	jClass := self.LoadClass("java/lang/Class")
	for _, class := range self.classMap {
		if class.jClass == nil {
			class.jClass = jClass.NewObject()
			class.jClass.extra = class
		}
	}
}

// ***********************************************************
// 加载基本类型的类
var primitiveTypes = map[string]string{
	"void":    "V",
	"boolean": "Z",
	"byte":    "B",
	"short":   "S",
	"int":     "I",
	"long":    "J",
	"char":    "C",
	"float":   "F",
	"double":  "D",
}

func (self *ClassLoader) loadPrimitiveClasses() {
	for primitiveType, _ := range primitiveTypes {
		class := &Class{
			accessFlags:      ACC_PUBLIC,
			name:             primitiveType,
			loader:           self,
			isInitialization: true,
		}
		class.jClass = self.classMap["java/lang/Class"].NewObject()
		class.jClass.extra = class
		self.classMap[primitiveType] = class
	}
}

// ***********************************************************

// 类加载过程
// 根据类名，返回Class对象
func (self *ClassLoader) LoadClass(className string) *Class {
	if class, exists := self.classMap[className]; exists {
		return class
	}
	var class *Class
	if className[0] == '[' {
		class = self.loadArrayClass(className)
	} else {
		class = self.loadNoArrayClass(className)
	}
	if jlClassClass, ok := self.classMap["java/lang/Class"]; ok {
		class.jClass = jlClassClass.NewObject()
		class.jClass.extra = class
	}
	return class
}

// 加载非数组类型的类，将其转化为Class对象返回
func (self *ClassLoader) loadNoArrayClass(className string) *Class {
	class := self.defineClass(className)
	link(class)
	if self.logClass {
		fmt.Printf("[Loaded %s]\n", className)
	}

	return class
}

func (self *ClassLoader) defineClass(className string) *Class {
	bytecode := self.getBytecode(className)
	class := parseClass(bytecode)
	class.loader = self
	resolveSuperClass(class)
	resolveInterfaces(class)
	self.classMap[class.name] = class
	return class
}

// 根据类名将类文件字节码数据读到切片中并返回 类名为java/lang/Object这种全类名
func (self *ClassLoader) getBytecode(className string) []U1 {
	data, err := self.classPath.GetBytecode(className)
	if err != nil {
		panic("java.lang.ClassNotFoundException:" + className)
	}
	byteCode := make([]U1, len(data))
	for i := 0; i < len(data); i++ {
		byteCode[i] = U1(data[i])
	}
	return byteCode
}

// 解析字节码，将其转为class对象
func parseClass(bytecode []U1) *Class {
	classfile, err := classfile.ParseBytecode(bytecode)
	if err != nil {
		panic("java.lang.ClassFormatError")
	}
	return newClass(*classfile)
}

// 递归加载参数类的父类
func resolveSuperClass(class *Class) {
	if class.name != "java/lang/Object" {
		class.superClass = class.loader.LoadClass(class.superClassName)
	}
}

// 递归加载参数类实现的接口
func resolveInterfaces(class *Class) {
	class.interfaces = make([]*Class, len(class.interfaceNames))
	for index, name := range class.interfaceNames {
		class.interfaces[index] = class.loader.LoadClass(name)
	}
}

// ***********************************************************
//类链接过程

func link(class *Class) {
	verify(class)
	prepare(class)
}

// 验证
func verify(class *Class) {

}

// 准备
func prepare(class *Class) {
	calcInstanceFieldSlotIds(class)
	calcStaticFieldSlotIds(class)
	allocAndInitStaticVars(class)
}

// 计算参数类的实例变量所占Slot槽的个数及各个变量在槽中的索引编号
func calcInstanceFieldSlotIds(class *Class) {
	slotId := U4(0)
	if class.superClass != nil {
		slotId = class.superClass.instanceSlotCount
	}
	fields := class.fields
	for i, field := range fields {
		if field.IsStatic() {
			continue
		}
		fields[i].slotId = slotId
		slotId++
		switch field.descriptor {
		case "J", "D":
			slotId++
		}
	}
	class.instanceSlotCount = slotId
}

// 计算参数类的静态变量所占Slot槽的个数及各个变量在槽中的索引编号
func calcStaticFieldSlotIds(class *Class) {
	slotId := U4(0)
	fields := class.fields
	for i, filed := range fields {
		if filed.IsStatic() {
			fields[i].slotId = slotId
			slotId++
			switch filed.descriptor {
			case "J", "D":
				slotId++
			}
		}
	}
	class.staticSlotCount = slotId
}

func allocAndInitStaticVars(class *Class) {
	class.staticVars = newSlots(class.staticSlotCount)
	for i, field := range class.fields {
		if field.IsStatic() && field.IsFinal() {
			initStaticFinalVar(class, &class.fields[i])
		}
	}
}

func initStaticFinalVar(class *Class, field *Field) {
	if field.constantValueIndex == 0 {
		return
	}
	index := field.slotId
	staticVars := class.staticVars
	constant := class.constantPool.GetConstant(field.constantValueIndex)

	switch field.descriptor {
	case "Z", "B", "C", "S", "I":
		staticVars.SetInt(index, constant.(I4))
	case "J":
		staticVars.SetLong(index, constant.(I8))
	case "F":
		staticVars.SetFloat(index, constant.(F4))
	case "D":
		staticVars.SetDouble(index, constant.(F8))
	case "Ljava/lang/String;":
		jStr := JString(class.loader, constant.(string))
		staticVars.SetRef(index, jStr)
	}
}

// ***********************************************************
// 加载数组类
func (self *ClassLoader) loadArrayClass(arrayName string) *Class {
	class := &Class{
		accessFlags:      ACC_PUBLIC,
		name:             arrayName,
		loader:           self,
		isInitialization: true,
		superClass:       self.LoadClass("java/lang/Object"),
		interfaces: []*Class{
			self.LoadClass("java/lang/Cloneable"),
			self.LoadClass("java/io/Serializable"),
		},
	}
	self.classMap[arrayName] = class
	return class
}

// ***********************************************************
